/**
 * Copyright © 2016-2025 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.terracloud.server.controller;

import com.google.common.util.concurrent.*;
import com.terracloud.server.common.data.id.DeviceId;
import com.terracloud.server.common.data.log.LogLevel;
import com.terracloud.server.common.data.log.LogType;
import com.terracloud.server.common.data.log.TerminalLog;
import com.terracloud.server.common.data.log.TerminalStatistics;
import com.terracloud.server.common.data.page.PageData;
import com.terracloud.server.common.data.page.TimePageLink;
import com.terracloud.server.config.annotations.ApiOperation;
import com.terracloud.server.queue.util.TbCoreComponent;
import com.terracloud.server.service.log.TerminalLogService;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.UUID;

import static com.terracloud.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static com.terracloud.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;

@RestController
@TbCoreComponent
@RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "终端日志", description = "终端日志管理API")
public class TerminalLogController extends BaseController {

    private final TerminalLogService terminalLogService;

    @ApiOperation(value = "保存终端日志 (Save Terminal Log)",
            notes = "创建或更新终端日志。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "终端日志已成功保存",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = TerminalLog.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/terminal/log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> saveTerminalLog(@RequestBody TerminalLog terminalLog) throws Exception {
        ListenableFuture<TerminalLog> future = toListenableFuture(() -> terminalLogService.saveTerminalLog(terminalLog));
        return handleTerminalLogFuture(future);
    }

    @ApiOperation(value = "获取终端日志 (Get Terminal Log)",
            notes = "根据日志ID获取终端日志。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "终端日志",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = TerminalLog.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "404", description = "终端日志未找到"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/terminal/log/{logId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getTerminalLogById(@PathVariable("logId") String strLogId) throws Exception {
        checkParameter("logId", strLogId);
        UUID logId = toUUID(strLogId);
        ListenableFuture<TerminalLog> future = toListenableFuture(() -> terminalLogService.findTerminalLogById(new com.terracloud.server.common.data.id.TerminalLogId(logId)));
        return handleTerminalLogFuture(future);
    }

    @ApiOperation(value = "删除终端日志 (Delete Terminal Log)",
            notes = "根据日志ID删除终端日志。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "终端日志已成功删除"),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "404", description = "终端日志未找到"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/terminal/log/{logId}", method = RequestMethod.DELETE)
    @ResponseStatus(value = HttpStatus.OK)
    public DeferredResult<ResponseEntity<?>> deleteTerminalLog(@PathVariable("logId") String strLogId) throws Exception {
        checkParameter("logId", strLogId);
        UUID logId = toUUID(strLogId);
        ListenableFuture<Void> future = toListenableFuture(() -> {
            terminalLogService.deleteTerminalLog(new com.terracloud.server.common.data.id.TerminalLogId(logId));
            return null;
        });
        return handleVoidFuture(future);
    }

    @ApiOperation(value = "获取终端日志列表 (Get Terminal Logs)",
            notes = "获取所有符合条件的终端日志列表。" + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "终端日志分页数据",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = PageData.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/terminal/logs", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getTerminalLogs(
            @RequestParam int pageSize,
            @RequestParam int page,
            @RequestParam(required = false) String textSearch,
            @RequestParam(required = false) String sortProperty,
            @RequestParam(required = false) String sortOrder,
            @RequestParam(required = false) Long startTime,
            @RequestParam(required = false) Long endTime,
            @RequestParam(required = false) String logType,
            @RequestParam(required = false) String logLevel,
            @RequestParam(required = false) String deviceId) throws Exception {
        
        TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
        ListenableFuture<PageData<TerminalLog>> future;
        log.debug("Getting terminal logs: pageSize={}, page={}, textSearch={}, sortProperty={}, sortOrder={}, startTime={}, endTime={}, logType={}, logLevel={}, deviceId={}",
                pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime, logType, logLevel, deviceId);
        
        // 修复查询逻辑 - 确保正确处理所有参数组合
        if (deviceId != null && !deviceId.isEmpty()) {
            DeviceId device = new DeviceId(toUUID(deviceId));
            if (logType != null && !logType.isEmpty()) {
                LogType type = LogType.valueOf(logType);
                log.debug("Calling findTerminalLogsByTenantIdAndDeviceIdAndType with tenantId={}, deviceId={}, logType={}", getCurrentUser().getTenantId(), device, type);
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByTenantIdAndDeviceIdAndType(getCurrentUser().getTenantId(), device, type, pageLink));
            } else if (logLevel != null && !logLevel.isEmpty()) {
                LogLevel level = LogLevel.valueOf(logLevel);
                log.debug("Calling findTerminalLogsByTenantIdAndDeviceIdAndLevel with tenantId={}, deviceId={}, logLevel={}", getCurrentUser().getTenantId(), device, level);
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByTenantIdAndDeviceIdAndLevel(getCurrentUser().getTenantId(), device, level, pageLink));
            } else {
                log.debug("Calling findTerminalLogsByDeviceId with tenantId={}, deviceId={}", getCurrentUser().getTenantId(), device);
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByDeviceId(getCurrentUser().getTenantId(), device, pageLink));
            }
        } else {
            if (logType != null && !logType.isEmpty()) {
                LogType type = LogType.valueOf(logType);
                log.debug("Calling findTerminalLogsByTenantIdAndType with tenantId={}, logType={}", getCurrentUser().getTenantId(), type);
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByTenantIdAndType(getCurrentUser().getTenantId(), type, pageLink));
            } else if (logLevel != null && !logLevel.isEmpty()) {
                LogLevel level = LogLevel.valueOf(logLevel);
                log.debug("Calling findTerminalLogsByTenantIdAndLevel with tenantId={}, logLevel={}", getCurrentUser().getTenantId(), level);
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByTenantIdAndLevel(getCurrentUser().getTenantId(), level, pageLink));
            } else {
                log.debug("Calling findTerminalLogsByTenantId with tenantId={}", getCurrentUser().getTenantId());
                future = toListenableFuture(() -> terminalLogService.findTerminalLogsByTenantId(getCurrentUser().getTenantId(), pageLink));
            }
        }
        
        return handlePageDataFuture(future);
    }

    @ApiOperation(value = "获取终端日志统计信息 (Get Terminal Log Statistics)",
            notes = "获取终端日志的所有统计信息。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "终端日志统计信息",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = TerminalStatistics.class))),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/terminal/logs/statistics", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getTerminalLogStatistics() throws Exception {
        ListenableFuture<TerminalStatistics> future = toListenableFuture(() -> terminalLogService.getTerminalStatistics(getCurrentUser().getTenantId()));
        return handleTerminalStatisticsFuture(future);
    }

    private <T> ListenableFuture<T> toListenableFuture(SupplierWithException<T> supplier) {
        SettableFuture<T> future = SettableFuture.create();
        try {
            T result = supplier.get();
            future.set(result);
        } catch (Exception e) {
            future.setException(e);
        }
        return future;
    }

    private DeferredResult<ResponseEntity<?>> handleTerminalLogFuture(ListenableFuture<TerminalLog> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(TerminalLog result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleVoidFuture(ListenableFuture<Void> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(Void result) {
                deferredResult.setResult(ResponseEntity.ok(HttpStatus.OK));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handlePageDataFuture(ListenableFuture<PageData<TerminalLog>> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(PageData<TerminalLog> result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleLongFuture(ListenableFuture<Long> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<Long>() {
            @Override
            public void onSuccess(Long result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleDoubleFuture(ListenableFuture<Double> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(Double result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleTerminalStatisticsFuture(ListenableFuture<TerminalStatistics> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(TerminalStatistics result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    @FunctionalInterface
    private interface SupplierWithException<T> {
        T get() throws Exception;
    }
}